package com.sakura.mybatis.session;

import com.sakura.mybatis.config.Configuration;
import com.sakura.mybatis.config.MappedStatement;
import com.sakura.mybatis.executor.Executor;

import java.lang.reflect.*;
import java.util.List;

public class DefaultSqlSession implements SqlSession {

    private final Configuration configuration;

    private final Executor executor;

    public DefaultSqlSession(Configuration configuration,
                             Executor executor) {
        this.configuration = configuration;
        this.executor = executor;
    }

    public Configuration getConfiguration() {
        return configuration;
    }

    public Executor getExecutor() {
        return executor;
    }

    @Override
    public <E> List<E> selectList(String mappedStatementId, Object param) {
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(mappedStatementId);
        if (mappedStatement == null) {
            throw new RuntimeException("未找到对应的mappedStatement");
        }
        return executor.query(configuration, mappedStatement, param);
    }

    @Override
    public <T> T selectOne(String mappedStatementId, Object param) {
        List<Object> list = this.selectList(mappedStatementId, param);
        if (list.size() == 1) {
            return (T) list.get(0);
        } else if (list.size() > 1) {
            throw new RuntimeException("selectOne but return result > 1");
        }
        return null;
    }

    @Override
    public <T> T getMapper(Class<T> mapperClass) {
        return (T) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{ mapperClass }, new DefaultInvocationHandler(configuration, this));
    }

    @Override
    public void close() {
        executor.close();
    }

    public static class DefaultInvocationHandler implements InvocationHandler {

        private final Configuration configuration;

        private final SqlSession sqlSession;

        public DefaultInvocationHandler(Configuration configuration,
                                        SqlSession sqlSession) {
            this.configuration = configuration;
            this.sqlSession = sqlSession;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            String nameSpace = method.getDeclaringClass().getName();
            String methodName = method.getName();
            String mappedStatementId = nameSpace + "." + methodName;
            MappedStatement mappedStatement = configuration.getMappedStatementMap().get(mappedStatementId);
            String sqlCommandType = mappedStatement.getSqlCommandType();
            switch (sqlCommandType) {
                case "select" : {
                    Type returnType = method.getGenericReturnType();
                    // 判断是否实现了 泛型参数化
                    if (returnType instanceof ParameterizedType) {
                        if (args != null) {
                            return sqlSession.selectList(mappedStatementId, args[0]);
                        }
                        return sqlSession.selectList(mappedStatementId, null);
                    }
                    return sqlSession.selectOne(mappedStatementId, args[0]);
                }

                case "update" : {
                    break;
                }

                case "insert" :  {
                    break;
                }

                case "delete" : {
                    break;
                }
            }
            return null;
        }
    }
}
